package dkbta.ontology.elements.abstractions.state;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

import dkbta.ontology.Ontology;
import dkbta.ontology.elements.ElementDef;
import dkbta.ontology.elements.ElementType;
import dkbta.ontology.elements.TypedName;
import dkbta.ontology.elements.abstractions.AbstractionDef;
import dkbta.ontology.elements.abstractions.TemporalSemanticProperty;
import dkbta.ontology.elements.abstractions.interpolation.PersistenceFunction;
import dkbta.ontology.loading.OntologyException;
import dkbta.ontology.values.ScaledSymbolicValues;
import dkbta.processing.ActionTimer;
import dkbta.processing.Patient;

public class StateDef extends AbstractionDef{
	private final StateMappingFunction _mappingFunction;

	/** The elements the state is abstracted from */
	private final TypedName[] _abstractedFrom;

	/**
	 * Initializes an ontology element with the given name and component type
	 * 
	 * @param name The element's name
	 * @param componentType The element's component type
	 * @param possibleValues The abstraction's possible values
	 * @param abstractedFrom The elements the abstraction is abstracted from
	 * @param necessaryContexts The contexts needed for the abstraction
	 * @param tempSemProperties The temporal semantics properties of this abstraction
	 * @param mapFunc The state's mapping function
	 */
	public StateDef(String name, String componentType,
		ScaledSymbolicValues possibleValues, Set<TypedName> abstractedFrom,
		Set<String> necessaryContexts, Set<TemporalSemanticProperty> tempSemProperties,
		StateMappingFunction mapFunc, PersistenceFunction persistFunc){
		super(ElementType.STATE, name, componentType, possibleValues, necessaryContexts,
				tempSemProperties, persistFunc);
		_abstractedFrom = abstractedFrom.toArray(new TypedName[abstractedFrom.size()]);
		_mappingFunction = mapFunc;
	}

	@Override
	public void validate(Ontology ontology) throws OntologyException{
		super.validate(ontology);
		for (TypedName af : _abstractedFrom){
			ElementDef ed = ontology.getElement(af.getElementType(), af.getName());
			if (ed == null){
				throw new OntologyException(
						"The state if abstracted from a non-existing element: " + af);
			}
			_prerequisiteElements.add(ed);
		}
		_mappingFunction.validate(ontology);
	}

	@Override
	public State newInstance(long startTime, long endTime, String[] value){
		Object validatedValue;
		if (value.length == 0
				|| (validatedValue = getValues().parseAndValidate(value[0])) == null){
			throw new IllegalArgumentException("Invalid value: " + Arrays.toString(value));
		}
		return new State(_name, startTime, endTime, validatedValue);
	}

	@Override
	public String completeToString(){
		StringBuilder sb = new StringBuilder(super.completeToString());
		sb.append("\r\nAbstracted From:");
		for (TypedName absFromElement : _abstractedFrom){
			sb.append("\r\n\t");
			sb.append(absFromElement);
		}
		sb.append("\r\nMapping Function: ").append(_mappingFunction);
		sb.append("\r\n");
		return sb.toString();
	}

	@Override
	protected void compute(Patient p){
		List<State> states = p.getStates(_name);

		ActionTimer actionTimer = ActionTimer.getInstance();

		// Performing the mapping
		int mappingActionId = actionTimer.actionStart();
		_mappingFunction.performMapping(p, states, _name, _abstractedFrom,
			_necessaryContexts);
		actionTimer.actionEnd(mappingActionId, p.getPatientId(), _name, _type,
			ActionTimer.Action.MAPPING);

		// Performing the interpolation
		int interpolationActionId = actionTimer.actionStart();
		performInterpolation(states);
		actionTimer.actionEnd(interpolationActionId, p.getPatientId(), _name, _type,
			ActionTimer.Action.INTERPOLATION);
	}

}
